﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CMSearchr
{
    using System.Collections;
    using System.DirectoryServices;
    using System.DirectoryServices.ActiveDirectory;
    using System.Drawing.Text;

    using Microsoft.NetEnterpriseServers;

    /// <summary>
    /// The form searchr.
    /// </summary>
    public partial class FormSearchr : Form
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FormSearchr"/> class.
        /// </summary>
        public FormSearchr()
        {
            this.InitializeComponent();

            // Initialise ArrayList
            this.DomainControllers = new ArrayList();
            this.Servers = new ArrayList();

            // Begin searching for domain controllers
            this.bwEnumControllers.RunWorkerAsync();
        }

        /// <summary>
        /// Gets the array containing domain controllers.
        /// </summary>
        public ArrayList DomainControllers { get; private set; }

        public ArrayList Servers { get; private set; }

        /// <summary>
        /// The bw enum controllers do work.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void BwEnumControllersDoWork(object sender, DoWorkEventArgs e)
        {
            this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", "Discovering domain controllers in domain."));
            this.BeginInvoke(
                (MethodInvoker)delegate
                    {
                        this.pbProgress.Style = ProgressBarStyle.Marquee;
                    });

            try
            {
                // Get the current domain
                var domain = Domain.GetComputerDomain();

                using (domain)
                {
                    // Loop through domain controllers in the current domain
                    foreach (DomainController dc in domain.FindAllDiscoverableDomainControllers())
                    {
                        using (dc)
                        {
                            this.DomainControllers.Add(dc.Name);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                this.BeginInvoke(
                    (MethodInvoker)delegate
                        {
                            this.pbProgress.Style = ProgressBarStyle.Blocks;
                            var topmsg = "Unable to enumerate domain controllers in the current computers domain...";
                            var exbox = new ApplicationException(topmsg, exception);
                            exbox.Source = this.Text;


                            var box = new ExceptionMessageBox(exbox);
                            box.Symbol = ExceptionMessageBoxSymbol.Error;
                            box.Buttons = ExceptionMessageBoxButtons.RetryCancel;

                            if (DialogResult.Retry == box.Show(this))
                            {
                                this.bwEnumControllers.RunWorkerAsync();
                            }
                            else
                            {
                                Application.ExitThread();
                            }
                        });
            }
        }

        /// <summary>
        /// The bw enum controllers run worker completed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void BwEnumControllersRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Loop through ArrayList and add to combo box
            this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", "Adding results to the form..."));
            if (this.DomainControllers.Count > 0)
            {
                foreach (var dc in this.DomainControllers)
                {
                    this.cbDomainControllers.Items.Add(dc);
                }

                // Enable controls and update labels
                this.cbDomainControllers.Enabled = true;
                this.bSearch.Enabled = true;
                this.pbProgress.Style = ProgressBarStyle.Blocks;
                this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", string.Empty));
            }
        }

        private void BwEnumControllersProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", e.UserState));
        }

        private void UpdateThreadLabel(Label label, string value)
        {
            this.BeginInvoke(
                (MethodInvoker)delegate
                    {
                        label.Text = value;
                    });
        }

        private void BwServerSearchDoWork(object sender, DoWorkEventArgs e)
        {
            var worker = sender as BackgroundWorker;
            
            // Check a cancellation is not pending
            if (worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", "Performing Active Directory search..."));
                using (var ds = new DirectorySearcher(this.tbFilter.Text, new[] { "cn" }))
                {
                    SearchResultCollection resultCollection = ds.FindAll();
                    foreach (SearchResult searchResult in resultCollection)
                    {
                        foreach (string propertyKey in searchResult.Properties.PropertyNames)
                        {
                            var values = searchResult.Properties[propertyKey];

                            foreach (var propertyValue in values)
                            {
                                if (propertyKey == "cn")
                                {
                                    this.Servers.Add(propertyValue.ToString());
                                }
                            }
                        }
                    }
                }

                // Reorder the servers array list
                this.Servers.Sort();

                this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", "Found " + this.Servers.Count + " servers matching criteria..."));
                int i = 0;
                foreach (string server in this.Servers)
                {
                    this.UpdateThreadLabel(
                        this.lProgress,
                        string.Format("Progress: {0}", "Running detection on " + server + "..."));
                    var result = DetectionEngine.PerformDetection(server);
                    i++;

                    if (result.Count != 0)
                    {
                        this.BeginInvoke(
                            (MethodInvoker)delegate
                                {
                                    var listViewItem = new ListViewItem(result["ServerName"].ToString(), 0);
                                    listViewItem.SubItems.Add(result["SiteCode"].ToString());
                                    listViewItem.SubItems.Add(result["SiteType"].ToString());
                                    listViewItem.SubItems.Add(result["SiteVersion"].ToString());
                                    this.lvResults.Items.Add(listViewItem);

                                    // Resize column headers
                                    this.ResizeListViewColumns(this.lvResults);
                                });
                    }

                    // Update progress
                    this.bwServerSearch.ReportProgress(i * 100 / this.Servers.Count);
                }
            }
        }

        private void BwServerSearchProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.pbProgress.Value = e.ProgressPercentage;
            this.lPercent.Text = string.Format("{0}%", e.ProgressPercentage);
        }

        private void BwServerSearchRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.bSearch.Enabled = true;
            this.pbProgress.Value = 0;
            this.UpdateThreadLabel(this.lProgress, string.Format("Progress: {0}", "Detection completed, see results below..."));
            this.UpdateThreadLabel(this.lPercent, string.Format("{0}%", 0));
        }

        private void BSearchClick(object sender, EventArgs e)
        {
            // Validate the combo box
            if (string.IsNullOrWhiteSpace(this.cbDomainControllers.SelectedItem.ToString()))
            {
                throw new ArgumentException("You must specify a domain controller before beginning the search.");
            }

            // Validate the LDAP filter
            if (string.IsNullOrWhiteSpace(this.tbFilter.Text))
            {
                throw new ArgumentException("You must specify a valid LDAP filter.");
            }

            if (!this.bwServerSearch.IsBusy)
            {
                // Begin searching the domain
                this.bSearch.Enabled = false;
                this.lvResults.Items.Clear();
                this.bwServerSearch.RunWorkerAsync();
            }
        }

        private void ResizeListViewColumns(ListView listView)
        {
            this.BeginInvoke((MethodInvoker)delegate
                {
                    foreach (ColumnHeader column in listView.Columns)
                    {
                        column.Width = -2;
                    }
                });

        }
    }
}
